package com.lagou.edu.factory;

import com.lagou.edu.context.ApplicationContextUtil;
import com.lagou.edu.context.annotation.Transactional;
import com.lagou.edu.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 代理对象工厂：生成代理对象的
 *
 * @author jzy
 */
public class DefaultProxyFactory implements ProxyFactory {

    @Override
    public Object createProxyObject(Object obj) {
        Class<?>[] interfaces = obj.getClass().getInterfaces();
        if (interfaces.length <= 0) {
            return this.getCglibProxy(obj);
        }
        return this.getJdkProxy(obj);
    }

    /**
     * Jdk动态代理
     *
     * @param obj 委托对象
     * @return 代理对象
     */
    public Object getJdkProxy(Object obj) {
        // 获取代理对象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), (proxy, method, args) -> DefaultProxyFactory.this.doInvoke(obj, method, args));
    }


    /**
     * 使用cglib动态代理生成代理对象
     *
     * @param obj 委托对象
     * @return 代理对象
     */
    public Object getCglibProxy(Object obj) {
        return Enhancer.create(obj.getClass(), (MethodInterceptor) (o, method, objects, methodProxy) -> DefaultProxyFactory.this.doInvoke(obj, method, objects));
    }

    /**
     * 拦截到到代理方法执行
     *
     * @param proxy  代理对象
     * @param method 拦截到的方法
     * @param args   传人方法的参数
     * @return 方法返回执行
     * @throws Throwable 执行过程中的异常
     */
    private Object doInvoke(Object proxy, Method method, Object[] args) throws Throwable {
        Transactional transactional = method.getAnnotation(Transactional.class);
        if (transactional == null) {
            return method.invoke(proxy, args);
        }
        Object result;
        TransactionManager transactionManager = ApplicationContextUtil.getBeanFactory().getObject(TransactionManager.class);
        try {
            // 开启事务(关闭事务的自动提交)
            transactionManager.beginTransaction();
            result = method.invoke(proxy, args);
            // 提交事务
            transactionManager.commit();
        } catch (Exception e) {
            // 回滚事务
            transactionManager.rollback();
            // 抛出异常便于上层servlet捕获
            throw e;
        }
        return result;
    }


}
